/*
 *  @Title ExcelUtil.java
 *  @Package： com.phoenix.core.util
 *  Copyright (c) 2017 by 江苏深南互联网金融信息服务有限公司  All right reserved
 */
package com.phoenix.core.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.DVConstraint;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.ss.util.CellRangeAddressList;

import com.phoenix.core.annotation.YYYYMMddHHmmss;
import com.phoenix.core.enums.CalculateType;
import com.phoenix.core.logger.SimpleLogger;
import com.phoenix.exception.NestedBusinessException;

/**
 *  @ClassName ExcelUtil
 *  @Description 导入导出工具类
 *  @author huangjx
 *  @version 1.0
 *  @date 2017年7月15日
 */
public class ExcelUtil<T> {

	private SimpleLogger logger = SimpleLogger.getLogger(ExcelUtil.class);
	private Class<T> cls;

	
	public ExcelUtil() {
	}
	
	public ExcelUtil(Class<T> cls) {
		this.cls = cls;
	}

	/**
	 * 将excel表单数据源的数据导入到list
	 * 
	 * @param fileName
	 *            工作表的名称
	 * @param input
	 *            java输入流
	 */
	public List<T> getExcelToList(String fileName, int sheetNum, InputStream input) throws IOException {
		List<T> list = new ArrayList<T>();
		Workbook book;
		try {
			book = WorkbookFactory.create(input);

		} catch (Exception e) {
			throw new RuntimeException("导入的文件格式不正确，请转换为97-2003格式的XLS格式，错误消息:" + e.getMessage(), e);
		}
		try {
			Sheet sheet = null;
			// 如果指定sheet名,则取指定sheet中的内容.
			sheet = book.getSheetAt(sheetNum);
			// 如果传入的sheet名不存在则默认指向第1个sheet.
			if (sheet == null) {
				sheet = book.getSheetAt(0);
			}
			// 得到数据的行数
			int rows = sheet.getLastRowNum();// 最后一行行标,比行数小1
			// 有数据时才处理
			if (rows <= 0) {
				return list;
			}
			// 得到类的所有field
			Field[] allFields = cls.getDeclaredFields();
			// 定义一个map用于存放列的序号和field
			Map<Integer, Field> fieldsMap = new HashMap<Integer, Field>();
			for (int i = 0; i < allFields.length; i++) {
				Field field = allFields[i];
				// 将有注解的field存放到map中
				if (field.isAnnotationPresent(Excel.class)) {
					Excel attr = field.getAnnotation(Excel.class);
					int col = i;
					// 根据指定的顺序获得列号
					if (StringUtils.isNotBlank(attr.column())) {
						col = getExcelCol(attr.column());
					}
					// 设置类的私有字段属性可访问
					field.setAccessible(true);
					fieldsMap.put(col, field);
				}
			}
			// 从第2行开始取数据,默认第一行是表头
			for (int i = 1; i <= rows; i++) {
				// 得到一行中的所有单元格对象.
				Row row = sheet.getRow(i);
				T entity = null;
				if (row == null) {
					continue;
				}
				for (int j = 0; j < row.getLastCellNum(); j++) {
					// 单元格中的内容.
					Cell cell = row.getCell(j);
					if (cell == null) {
						continue;
					}
					cell.setCellType(HSSFCell.CELL_TYPE_STRING);
					String c = parseExcel(cell);
					if (StringUtils.isBlank(c)) {
						continue;
					}
					// 如果不存在实例则新建
					entity = (entity == null ? cls.newInstance() : entity);
					// 从map中得到对应列的field
					Field field = fieldsMap.get(j);
					if (field == null) {
						continue;
					}
					// 取得类型,并根据对象类型设置值.
					Class<?> fieldType = field.getType();
					if (fieldType == null) {
						continue;
					} else {
						if (cell.getCellType() != HSSFCell.CELL_TYPE_STRING && fieldType == String.class) {
							c = c.replace(".00", ""); // 修正
							c = c.replace(".0", "");// 格式错误，，单元格希望是文本，但值可能是数字
						}
					}
					if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) {
						field.set(entity, Integer.parseInt(c));
					} else if (String.class == fieldType) {
						field.set(entity, String.valueOf(c));
					} else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) {
						field.set(entity, Long.valueOf(c));
					} else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) {
						field.set(entity, Float.valueOf(c));
					} else if ((Short.TYPE == fieldType) || (Short.class == fieldType)) {
						field.set(entity, Short.valueOf(c));
					} else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) {
						field.set(entity, Double.valueOf(c));
					} else if ((Date.class == fieldType)) {
						Date d = DateUtils.parseAuto(c);
						field.set(entity, d);
					} else if ((BigDecimal.class == fieldType)) {
						BigDecimal d = new BigDecimal(c);
						field.set(entity, d);
					} else if (Character.TYPE == fieldType) {
						if ((c != null) && (c.length() > 0)) {
							field.set(entity, Character.valueOf(c.charAt(0)));
						}
					}
				}
				if (entity != null) {
					list.add(entity);
				}
			}

		} catch (Exception e) {
			throw new NestedBusinessException("将excel表单数据源的数据导入到list异常!", e);
		}
		return list;
	}
	/**
	 * 将excel表单数据源中的每一行数据导入到list,以字符串数据形式存储
	 * 
	 * @param input
	 *            java输入流
	 */
	public List<String> getExcelToListStr(InputStream input) throws IOException {
		List<String> list = new ArrayList<String>();
		Workbook book;
		try {
			book = WorkbookFactory.create(input);
		} catch (Exception e) {
			throw new RuntimeException("导入的文件格式不正确，请转换为97-2003格式的XLS格式，错误消息:" + e.getMessage(), e);
		}
		try {
			Sheet sheet = null;
			// 如果指定sheet名,则取指定sheet中的内容.
			sheet = book.getSheetAt(0);
			// 如果传入的sheet名不存在则默认指向第1个sheet.
			if (sheet == null) {
				sheet = book.getSheetAt(0);
			}
			// 得到数据的行数
			int rows = sheet.getLastRowNum();// 最后一行行标,比行数小1
			// 有数据时才处理
			if (rows <= 0) {
				return list;
			}
			// 从第1行开始取数据
			for (int i = 0; i <= rows; i++) {
				// 得到一行中的所有单元格对象.
				Row row = sheet.getRow(i);
				StringBuilder sb = new StringBuilder();
				int colNum = row.getPhysicalNumberOfCells();
				for (int j = 0; j < colNum; j++) {
					// 单元格中的内容.
					Cell cell = row.getCell(j);
					if (cell == null) {
						sb.append(";");
						continue;
					}
					cell.setCellType(HSSFCell.CELL_TYPE_STRING);
					String c = parseExcel(cell);
					if (StringUtils.isBlank(c)) {
						sb.append(";");
						continue;
					}
					sb.append(c).append(";");
					
				}
				list.add(sb.toString());
			}

		} catch (Exception e) {
			throw new NestedBusinessException("将excel表单数据源的数据导入到list异常!", e);
		}
		return list;
	}

	/**
	 * 将list数据源的数据导入到excel表单
	 * 
	 * @param list
	 *            数据源
	 * @param sheetName
	 *            工作表的名称
	 * @param output
	 *            java输出流
	 */
	public void getListToExcel(List<T> list, String sheetName, OutputStream output) throws NestedBusinessException {
		try {
			// excel中每个sheet中最多有65536行
			int sheetSize = Short.MAX_VALUE -1;
			// 得到所有定义字段
			Field[] allFields = cls.getDeclaredFields();
			List<Field> fields = new ArrayList<Field>();
			// 得到所有field并存放到一个list中
			for (Field field : allFields) {
				if (field.isAnnotationPresent(Excel.class)) {
					fields.add(field);
				}
			}
			// 产生工作薄对象
			HSSFWorkbook workbook = new HSSFWorkbook();
			// 取出一共有多少个sheet
			int listSize = 0;
			if (list != null && list.size() >= 0) {
				listSize = list.size();
			}
			double sheetNo = Math.ceil(listSize / sheetSize);
			for (int index = 0; index <= sheetNo; index++) {
				// 产生工作表对象
				HSSFSheet sheet = workbook.createSheet();
				// 设置工作表的名称.
				workbook.setSheetName(index, sheetName + index);
				HSSFRow row;
				HSSFCell cell;// 产生单元格
				row = sheet.createRow(0);// 产生一行
				/* *********普通列样式********* */
				HSSFFont font = workbook.createFont();
				HSSFCellStyle cellStyle = workbook.createCellStyle();
				HSSFCellStyle newCellStyle2  = workbook.createCellStyle();
				font.setFontName("Arail narrow"); // 字体
				font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); // 字体宽度
				/* *********标红列样式********* */
				HSSFFont newFont = workbook.createFont();
				HSSFCellStyle newCellStyle = workbook.createCellStyle();
				newFont.setFontName("Arail narrow"); // 字体
				newFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD); // 字体宽度
				/* *************创建列头名称*************** */
				for (int i = 0; i < fields.size(); i++) {
					Field field = fields.get(i);
					Excel attr = field.getAnnotation(Excel.class);
					int col = i;
					// 根据指定的顺序获得列号
					if (StringUtils.isNotBlank(attr.column())) {
						col = getExcelCol(attr.column());
					}
					// 创建列
					cell = row.createCell(col);
					if (attr.isMark()) {
						newFont.setColor(HSSFFont.COLOR_RED); // 字体颜色
						newCellStyle.setFont(newFont);
						cell.setCellStyle(newCellStyle);
					} else {
						font.setColor(HSSFFont.COLOR_NORMAL); // 字体颜色
						cellStyle.setFont(font);
						cell.setCellStyle(cellStyle);
					}
					sheet.setColumnWidth(i,
							(int) ((attr.name().getBytes().length <= 4 ? 6 : attr.name().getBytes().length) * 1.5
									* 256));
					// 设置列中写入内容为String类型
					cell.setCellType(HSSFCell.CELL_TYPE_STRING);
					// 写入列名
					cell.setCellValue(attr.name());
					// 如果设置了提示信息则鼠标放上去提示.
					if (StringUtils.isNotBlank(attr.prompt())) {
						setHSSFPrompt(sheet, "", attr.prompt(), 1, 100, col, col);
					}
					// 如果设置了combo属性则本列只能选择不能输入
					if (attr.combo().length > 0) {
						setHSSFValidation(sheet, attr.combo(), 1, 100, col, col);
					}
				}
				/* *************创建内容列*************** */
				font = workbook.createFont();
				cellStyle = workbook.createCellStyle();
				newCellStyle2 = workbook.createCellStyle();
				int startNo = index * sheetSize;
				int endNo = Math.min(startNo + sheetSize, listSize);
				// 写入各条记录,每条记录对应excel表中的一行
				for (int i = startNo; i < endNo; i++) {
					row = sheet.createRow(i + 1 - startNo);
					T vo = (T) list.get(i); // 得到导出对象.
					for (int j = 0; j < fields.size(); j++) {
						// 获得field
						Field field = fields.get(j);
						// 设置实体类私有属性可访问
						field.setAccessible(true);
						Excel attr = field.getAnnotation(Excel.class);
						int col = j;
						// 根据指定的顺序获得列号
						if (StringUtils.isNotBlank(attr.column())) {
							col = getExcelCol(attr.column());
						}
						// 根据ExcelVOAttribute中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
						if (attr.isExport()) {
							// 创建cell
							cell = row.createCell(col);
							if (attr.isMark()) {
								newFont.setColor(HSSFFont.COLOR_RED); // 字体颜色
								newCellStyle.setFont(newFont);
								cell.setCellStyle(newCellStyle);
								newCellStyle2 = newCellStyle;
							} else {
								font.setColor(HSSFFont.COLOR_NORMAL); // 字体颜色
								cellStyle.setFont(font);
								cell.setCellStyle(cellStyle);
								newCellStyle2 = cellStyle;
							}
							// 如果数据存在就填入,不存在填入空格
							Class<?> classType = (Class<?>) field.getType();
							if (field.get(vo) != null && classType.isAssignableFrom(Date.class)) {
								SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
								String value = DateUtils.formatDate(sdf.parse(String.valueOf(field.get(vo))));
								if (field.getAnnotation(YYYYMMddHHmmss.class) != null) {
									value = DateUtils.formatDatetime((Date)field.get(vo));
								}
								cell.setCellValue(value);
							}else if (field.get(vo) != null && classType.isAssignableFrom(BigDecimal.class)) {
								cell.setCellValue(((BigDecimal) field.get(vo)).compareTo(BigDecimal.ZERO) == 0 ? 0L
										: new Double(String.valueOf(field.get(vo))));
								HSSFDataFormat format= workbook.createDataFormat();  
								newCellStyle2.setDataFormat(format.getFormat("#,##0.00"));  
					            cell.setCellStyle(newCellStyle2);  
							}else{
								cell.setCellValue(field.get(vo) == null ? "" : String.valueOf(field.get(vo)));
							}
						}
					}
				}
				/* *************创建合计列*************** */
				HSSFRow lastRow = sheet.createRow((short) (sheet.getLastRowNum() + 1));
				for (int i = 0; i < fields.size(); i++) {
					Field field = fields.get(i);
					Excel attr = field.getAnnotation(Excel.class);
					if (attr.isSum()) {
						int col = i;
						// 根据指定的顺序获得列号
						if (StringUtils.isNotBlank(attr.column())) {
							col = getExcelCol(attr.column());
						}
						BigDecimal totalNumber = BigDecimal.ZERO;
						for (int j = 1, len = (sheet.getLastRowNum() - 1); j < len; j++) {
							HSSFRow hssfRow = sheet.getRow(j);
							if (hssfRow != null) {
								HSSFCell hssfCell = hssfRow.getCell(col);
								if (hssfCell != null && hssfCell.getCellType() == HSSFCell.CELL_TYPE_STRING
										&& ValidateUtil.isFloat(hssfCell.getStringCellValue())) {
									totalNumber = BigDecimalUtils.getValue(totalNumber,
											BigDecimal.valueOf(Double.valueOf(hssfCell.getStringCellValue())),
											CalculateType.Add);
								}
							}
						}
						HSSFCell sumCell = lastRow.createCell(col);
						sumCell.setCellValue(new HSSFRichTextString("合计：" + totalNumber));
					}
				}
			}
			output.flush();
			workbook.write(output);
			output.close();
		} catch (Exception e) {
			logger.error("将list数据源的数据导入到excel表单异常!", e);
			throw new RuntimeException("将list数据源的数据导入到excel表单异常!", e);
		}

	}

	/**
	 * 将EXCEL中A,B,C,D,E列映射成0,1,2,3
	 * 
	 * @param col
	 */
	public static int getExcelCol(String col) {
		col = col.toUpperCase();
		// 从-1开始计算,字母重1开始运算。这种总数下来算数正好相同。
		int count = -1;
		char[] cs = col.toCharArray();
		for (int i = 0; i < cs.length; i++) {
			count += (cs[i] - 64) * Math.pow(26, cs.length - 1 - i);
		}
		return count;
	}

	/**
	 * 设置单元格上提示
	 * 
	 * @param sheet
	 *            要设置的sheet.
	 * @param promptTitle
	 *            标题
	 * @param promptContent
	 *            内容
	 * @param firstRow
	 *            开始行
	 * @param endRow
	 *            结束行
	 * @param firstCol
	 *            开始列
	 * @param endCol
	 *            结束列
	 * @return 设置好的sheet.
	 */
	public static HSSFSheet setHSSFPrompt(HSSFSheet sheet, String promptTitle, String promptContent, int firstRow,
			int endRow, int firstCol, int endCol) {
		// 构造constraint对象
		DVConstraint constraint = DVConstraint.createCustomFormulaConstraint("DD1");
		// 四个参数分别是：起始行、终止行、起始列、终止列
		CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
		// 数据有效性对象
		HSSFDataValidation data_validation_view = new HSSFDataValidation(regions, constraint);
		data_validation_view.createPromptBox(promptTitle, promptContent);
		sheet.addValidationData(data_validation_view);
		return sheet;
	}

	/**
	 * 设置某些列的值只能输入预制的数据,显示下拉框.
	 * 
	 * @param sheet
	 *            要设置的sheet.
	 * @param textlist
	 *            下拉框显示的内容
	 * @param firstRow
	 *            开始行
	 * @param endRow
	 *            结束行
	 * @param firstCol
	 *            开始列
	 * @param endCol
	 *            结束列
	 * @return 设置好的sheet.
	 */
	public static HSSFSheet setHSSFValidation(HSSFSheet sheet, String[] textlist, int firstRow, int endRow,
			int firstCol, int endCol) {
		// 加载下拉列表内容
		DVConstraint constraint = DVConstraint.createExplicitListConstraint(textlist);
		// 设置数据有效性加载在哪个单元格上,四个参数分别是：起始行、终止行、起始列、终止列
		CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
		// 数据有效性对象
		HSSFDataValidation data_validation_list = new HSSFDataValidation(regions, constraint);
		sheet.addValidationData(data_validation_list);
		return sheet;
	}

	/**
	 * 读取单元
	 * 
	 * @param cell
	 * @return
	 */
	private String parseExcel(Cell cell) {
		String result = new String();
		switch (cell.getCellType()) {
		case HSSFCell.CELL_TYPE_NUMERIC:// 数字类型
			if (HSSFDateUtil.isCellDateFormatted(cell)) {// 处理日期格式、时间格式
				SimpleDateFormat sdf = null;
				if (cell.getCellStyle().getDataFormat() == HSSFDataFormat.getBuiltinFormat("h:mm")) {
					sdf = new SimpleDateFormat("HH:mm");
				} else {// 日期
					sdf = new SimpleDateFormat("yyyy-MM-dd");
				}
				Date date = cell.getDateCellValue();
				result = sdf.format(date);
			} else if (cell.getCellStyle().getDataFormat() == 58) {
				// 处理自定义日期格式：m月d日(通过判断单元格的格式id解决，id的值是58)
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				double value = cell.getNumericCellValue();
				Date date = org.apache.poi.ss.usermodel.DateUtil.getJavaDate(value);
				result = sdf.format(date);
			} else {
				double value = cell.getNumericCellValue();
				CellStyle style = cell.getCellStyle();
				DecimalFormat format = new DecimalFormat();
				String temp = style.getDataFormatString();
				// 单元格设置成常规
				if (temp.equals("General")) {
					format.applyPattern(".##");
				}
				result = format.format(value);
			}
			break;
		case HSSFCell.CELL_TYPE_STRING:// String类型
			result = cell.getRichStringCellValue().toString();
			break;
		case HSSFCell.CELL_TYPE_BLANK:
			result = "";
		default:
			result = "";
			break;
		}
		return result;
	}
}
